; CoBra CP/M Game Loader ; ; CPU Type: Z80 ; ; Created for Linux z80-asm ; ; on 2017-02-09 09:17 ; ; ; This 512 byte code (loader) is used for loading ; a Spectrum Basic application from CP/M. ; The application is usually cracked by ; using a NMI button & NMI routine that saves ; the upper 48K of Spectrum memory with the ; "snapshot" of the application at that moment ; Then the 48K of code is copied to a CP/M disk, ; the code sequence below is attached in front ; of it and the result is saved as a .COM file ; on disk. When the executable .COM obtained this way ; is run under CP/M, it is loaded into memory ; starting at 0100h and then run from this ; address. So the sequence below is always ; run first. What it does is to move the actual ; application code (48K) from 0300-C2FF to ; 4000-FFFF, copy the Spectrum screen portion ; from the application snapshot into the video ; memory of CP/M (the game start screen usually), ; set the stack pointer and load the entry point ; of the application "snapshot" into the HL register ; and then change the hardware configuration to BASIC. ; After changing to BASIC config, execution will start ; at the entry point stored in HL. This is the entry point ; of the CPU RESTORE routine which must be inserted ; somewhere in the block of code saved by NMI, preferrably ; above the video memory so the screen doesn't get corrupted. ; The entry point must be chosen after examining the code ; to find an unused area in it, big enough to contain the ; CPU RESTORE routine. The entry point is at the same time ; the temporary stack base while CPU RESTORE is running. ORG $0100 JR START CPURES: DEFW $0000 ; BASIC ENTRY POINT (to CPU RESTORE) & TEMPORARY STACK POINTER (while executing CPU RESTORE) ; ########### ADJUST THIS VALUE TO ANY FREE ZONE WITHIN CODE ABOVE VIDEO MEM ########### ; ########### WHERE THE CPU RESTORE ROUTINE CAN BE INSERTED ########### START: DI LD HL,$C1FF ; > move 48K app code LD DE,$FFFF ; > from 0200-C1FF (this Game Loader takes up $100 bytes) LD BC,$C000 ; > to 4000-FFFF LDDR ; > (to std. Spectrum RAM area) LD BC,$1B00 ; Spectrum screen mem size LD HL,$4000 ; app screen data pointer LOOP1: LD D,(HL) ; <--- <--- <---| read 1 byte of app screen data (from DRAM#1) LD A,$40 ; | OUT ($FE),A ; set O6 to 1 | (CPU access to VRAM in CP/M) LD (HL),D ; | write data to CP/M screen mem XOR A ; | OUT ($FE),A ; set O6 to 0 | (CPU access to DRAM#1 in CP/M) INC HL ; | increment screen data pointer DEC BC ; | decrement Spectrum screen size counter LD A,B ; test if | OR C ; BC=00 | JR NZ,LOOP1 ; ---> ---> --->| restart loop if Spectrum screen size counter > 0 LD SP,(CPURES) ; set SP = entry point of CPU RESTORE LD HL,(CPURES) ; set HL = entry point of CPU RESTORE LD A,$03 ; OUT ($E3),A ; disable OUT ($EB),A ; interrupts OUT ($F3),A ; from OUT ($FB),A ; Z80-CTC XOR A ; OUT ($FD),A ; send invalid command to 8272 (set to standby) LOOP2: LD A,$00 ; <--- <--- <--- <--- <--- <----| IN A, ($FE) ; read keyboard | AND $3F ; | CP $3F ; test if any key pressed | JR NZ,SKIP ; exit loop if key pressed | JR LOOP2 ; ----> ---> ---> ---> ---> --->| SKIP: EXX ; preserve HL = app entry point LD HL,$0038 ; start addr of BASIC loading routine in BOOT ROM LD A,$C1 ; set SO to 1, O6 to 1, border to blue OUT ($FE),A ; (SO selects BASIC in a 32K ROM, O6=1 for startup hw config) LD R,A ; set bit 7 of R to 1 for startup hw config JP (HL) ; after changing to startup hw config, jump to 0038h